package cn.mrcode.wxsdk.core.dialogue.common.accessToken.lifeCycle.distributed.strategy.master;

import cn.mrcode.wxsdk.core.dialogue.common.log.LogTemplateUtil;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.recipes.leader.LeaderSelector;
import org.apache.curator.framework.recipes.leader.LeaderSelectorListenerAdapter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.Closeable;
import java.io.IOException;

/**
 * 生命周期 curator leader模版框架监听器
 * https://yq.aliyun.com/articles/18275 一问中说道：自定义实现Closeable会让leader出现异常的时候关闭释放掉leader领导权
 *
 * @author zhuqiang
 * @version V1.0
 * @date 2016/6/29
 */
abstract public class LifeCycleLeaderSelectorListener extends LeaderSelectorListenerAdapter implements Closeable {
    private static Logger log = LoggerFactory.getLogger(AccessTokenDistributedTaskMaster.class);
    private CuratorFramework client;
    private LeaderSelector selector;
    private String id;
    private String leaderPath;

    /**
     * @param client
     * @param leaderPath 维护的根目录path 如： "/basetoken"
     * @param id
     */
    public LifeCycleLeaderSelectorListener(CuratorFramework client, String leaderPath, String id) {
        LeaderSelector selector = new LeaderSelector(client, leaderPath, this);
        this.client = client;
        this.id = id;
        this.leaderPath = leaderPath;
        selector.setId(id); // 在该目录下会存在写入的数据
        selector.autoRequeue();
        selector.start();
    }

    @Override
    public void takeLeadership(CuratorFramework curatorFramework) throws Exception {
        log.info(LogTemplateUtil.svMsg("", "%s 成为master角色,将接替更新任务", id));
        String svData = new String(curatorFramework.getData().forPath(leaderPath));
        log.info(LogTemplateUtil.svMsg("", "%s 拉取zk数据=%s", id, svData));
        refreshData(svData);
        readyTaskData();
        run();
    }

    @Override
    public void close() throws IOException {
        selector.close();
    }

    /** 释放leader权 */
    public void leaderClose() {
        try {
            selector.close();
        } catch (Exception e) {
            log.error("leader close 异常：" + e.getMessage());
        }
    }

    /**
     * 反序列化zk数据并更新到本地缓存
     *
     * @param data zk数据
     */
    abstract protected void refreshData(String data);

    /**
     * 成为leader的时候，需要准备维护的任务数据
     * 1. 以zk的的数据为主
     * 2. 对比本地配置文件中需要维护的数据，zk上不存在的则添加进zk（这里在去掉删减维护数据的时候，就需要先手动重置下zk上的数据了）
     */
    abstract protected void readyTaskData() throws Exception;

    /** 作为leader的任务更新机制，相当于一个在线程里面不断监听过期数据，然后进行更新的操作 */
    abstract protected void run();
}
